#ifndef _Ape__ShPtr_h
#define _Ape__ShPtr_h

#include <Ape/Object.h>
#include <Ape/String.h>
#include <Ape/UInt.h>

#include <Ape/ENullPointer.h>
#include <Ape/EInvalidOperation.h>

namespace Ape {
	template <class C_t>
	class ShPtr : public Object {
		TYPE_NAME ("Ape::ShPtr<C_t>")
	private:
		mutable const Object* m_Object;
	public:
		ShPtr();
		ShPtr (const ShPtr<C_t>& other);
		ShPtr (const C_t* obj);
		
		virtual ~ShPtr();
		
		operator C_t* () const;
		C_t* operator -> () const;
		C_t& operator * () const;
		const C_t* operator = (const C_t* p);
		C_t* operator = (C_t* p);
		C_t* Release() const;
		
		void Unlink();
		void Link(const C_t* p);
		
		virtual const String ToString() const;
	};
	
	template <class C_t>
	const C_t* ShPtr<C_t>::operator = (const C_t* p) {
		Unlink();
		Link(p);
		return p;
	}
	template <class C_t>
	C_t* ShPtr<C_t>::operator = (C_t* p) {
		Unlink();
		Link(p);
		return p;
	}
	
	template <class C_t>
	void ShPtr<C_t>::Link(const C_t* p) {
		m_Object = p;
		if (m_Object)
			m_Object->__AddRef();
	}
	
	template <class C_t>
	void ShPtr<C_t>::Unlink() {
		if (m_Object && !m_Object->__RemRef() ) {
			del (m_Object);
		}
	}
	
	template <class C_t>
	ShPtr<C_t>::ShPtr()
			: m_Object (NULL)
	{}
	
	template <class C_t>
	ShPtr<C_t>::ShPtr (const ShPtr<C_t>& other)
			: Object (other),
			m_Object (other.m_Object)
	{
		if (m_Object)
			m_Object->__AddRef();
	}
	
	template <class C_t>
	ShPtr<C_t>::ShPtr (const C_t* obj)
	{
		Link(obj);
	}
	
	template <class C_t>
	ShPtr<C_t>::~ShPtr() {
		Unlink();
	}
	
	template <class C_t>
	ShPtr<C_t>::operator C_t* () const {
		return (C_t*)m_Object;
	}
	template <class C_t>
	C_t* ShPtr<C_t>::operator -> () const {
		if (!m_Object)
			throw ENullPointer (L"Attempt to call a member of a NULL pointer");
		return (C_t*) m_Object;
	}
	template <class C_t>
	C_t& ShPtr<C_t>::operator * () const {
		if (!m_Object)
			throw ENullPointer (L"Attempt to derefference a NULL pointer");
		return * ( (C_t*) m_Object);
	}
	template <class C_t>
	C_t* ShPtr<C_t>::Release() const {
		if (!m_Object)
			throw Ape::EInvalidOperation(L"Attempt to release when NULL");
		C_t* p = m_Object;
		m_Object->__RemRef();
		m_Object = NULL;
		return p;
	}
	
	
	template<typename C_t>
	const String ShPtr<C_t>::ToString() const {
		return
			String (L"Ape::ShPtr<") +
			String (C_t::getTypeName() ) +
			String (L"> ") +
			(m_Object ?
			 (L"[0x" + UInt (uint (m_Object) ).ToString (radixHex) + L"] ")
			 :
			 String (L"[nil]")
			);
	}
	
}

#endif // _Ape__ShPtr_h
